#Written by MMCOWBOY
import pygame
import math

def get_offsets(original_length, degree):
    while (degree > 360):
        degree -= 360
    while (degree < 0):
        degree += 360
    if (degree == 0):
        return [0, -original_length]
    elif (degree == 90.0):
        return [original_length, 0]
    elif (degree == 180.0):
        return [0, original_length]
    elif (degree == 270.0):
        return [-original_length, 0]
    else:
        t_sin_abs = math.fabs(math.sin(degree_to_angle(degree)))
        t_cos_abs = math.fabs(math.cos(degree_to_angle(degree)))
        offset_x = t_sin_abs * original_length
        offset_y = t_cos_abs * original_length
        if (degree > 0 and degree < 90):
            return offset_x, -offset_y
        elif (degree > 90 and degree < 180):
            return offset_x, offset_y
        elif (degree > 180 and degree < 270):
            return -offset_x, offset_y
        else:
            return -offset_x, -offset_y
    

def angle_to_degree(angle):
    while (angle < 0):
        angle += 2.0 * math.pi
    degree = (angle / math.pi) * 180.0
    return degree

def degree_to_angle(degree):
    
    while (degree >= 360):
        degree -= 360
    
    while (degree < 0):
        degree += 360
        
    angle = (degree * math.pi) / 180.0
    return angle

def get_distance(sprite1, sprite2):
    # count the center distance between two sprites
    return math.sqrt(math.fabs(sprite1.rect.centerx - sprite2.rect.centerx) ** 2
                     + math.fabs(sprite1.rect.centery - sprite2.rect.centery) ** 2)

def get_angle(dx, dy):
    # given dx and dy, return their merged vector's angle
    if (dx == 0):
        if (dy > 0):
            return math.pi
        else: # dy <= 0
            return 0
    else:
        if (dy > 0):
            t_angle = math.atan(float(dx) / float(dy))
            if (dx > 0):
                return math.pi - t_angle
            else: # dx < 0
                return -(math.pi + t_angle)
        elif (dy == 0):
            if (dx > 0):
                return float(math.pi / 2.0)
            else: # dx < 0
                return -float(math.pi / 2.0)
        else: # dy < 0
            t_angle = math.atan(float(dx) / float(dy))
            return -t_angle
        
def get_angle_points(fromp, destp):
    p1x = fromp[0]
    p1y = fromp[1]
    p2x = destp[0]
    p2y = destp[1]
    
    dx = p2x - p1x
    dy = p2y - p1y
    
    return get_angle(dx, dy)

def line_in_area(fromp, destp1, destp2, testp):
    angle1 = get_angle_points(fromp, destp1)
    angle2 = get_angle_points(fromp, destp2)
    angle3 = get_angle_points(fromp, testp)
    
    if angle1 > angle2:
        tangle = angle1
        angle1 = angle2
        angle2 = tangle
    
    if angle1 <= angle3 and angle3 <= angle2:
        return True
    return False

def laser_in_rect(laser, rect):
    tdegree = degree_pro(-laser.current_degree)
    if tdegree == 0.0:
         return line_in_area( (laser.rect.centerx,laser.rect.bottom),\
                            (rect.left, rect.bottom),\
                            (rect.right, rect.bottom),\
                            (laser.rect.centerx, laser.rect.centery) )
    elif tdegree == 90.0:
        return line_in_area( (laser.rect.left,laser.rect.centery),\
                            (rect.left, rect.top),\
                            (rect.left, rect.bottom),\
                            (laser.rect.centerx, laser.rect.centery) )
    elif tdegree == 180.0:
        return line_in_area( (laser.rect.centerx,laser.rect.top),\
                            (rect.left, rect.top),\
                            (rect.right, rect.top),\
                            (laser.rect.centerx, laser.rect.centery) )
    elif tdegree == 270.0:
        return line_in_area( (laser.rect.right,laser.rect.centery),\
                            (rect.right, rect.top),\
                            (rect.right, rect.bottom),\
                            (laser.rect.centerx, laser.rect.centery) )
    else:
        if tdegree > 0.0 and tdegree < 90.0:
            return line_in_area( (laser.rect.left,laser.rect.bottom),\
                                (rect.left, rect.top),\
                                (rect.right, rect.bottom),\
                                (laser.rect.centerx, laser.rect.centery) )
        elif tdegree > 90.0 and tdegree < 180.0:
            return line_in_area( (laser.rect.left,laser.rect.top),\
                                (rect.right, rect.top),\
                                (rect.left, rect.bottom),\
                                (laser.rect.centerx, laser.rect.centery) )
        elif tdegree > 180.0 and tdegree < 270.0:
            return line_in_area( (laser.rect.right,laser.rect.top),\
                                (rect.left, rect.top),\
                                (rect.right, rect.bottom),\
                                (laser.rect.centerx, laser.rect.centery) )
        else:
            return line_in_area( (laser.rect.right,laser.rect.bottom),\
                                (rect.right, rect.top),\
                                (rect.left, rect.bottom),\
                                (laser.rect.centerx, laser.rect.centery) )

def degree_pro(degree):
    tdegree = degree
    while tdegree < 0.0:
        tdegree += 360.0
    while tdegree >= 360.0:
        tdegree -= 360.0
    return tdegree

def angle_pro(angle):
    tangle = angle
    while tangle < 0.0:
        tangle += math.pi * 2
    while tangle >= math.pi * 2:
        tangle -= math.pi * 2
    return tangle

        
def trans_dx_dy_angle(dx, dy, angel):
    # given dx and dy, and an angel, return the merged vector's dx' and dy'
    trans_angel = get_angle(dx,dy) + angel
    while (trans_angel >= (math.pi * 2)):
        trans_angel -= (math.pi * 2)
    while (trans_angel < 0):
        trans_angel += (math.pi * 2)
    merge_xy = math.sqrt(dx ** 2 + dy ** 2)
    if (trans_angel == 0):
        return [0,-merge_xy]
    elif (trans_angel == math.pi):
        return [0,merge_xy]
    elif (trans_angel == (math.pi / 2.0)):
        return [merge_xy,0]
    elif (trans_angel == (math.pi * 3 / 2.0)):
        return [-merge_xy,0]
    else:
        if (trans_angel > 0 and trans_angel < math.pi / 2.0):
            tdx = merge_xy * math.sin(trans_angel)
            tdy = -math.sqrt(merge_xy ** 2 - tdx ** 2)
            return [tdx,tdy]
        elif (trans_angel > math.pi / 2.0 and trans_angel < math.pi):
            trans_angel = math.pi - trans_angel
            tdx = merge_xy * math.sin(trans_angel)
            tdy = math.sqrt(merge_xy ** 2 - tdx ** 2)
            return [tdx,tdy]
        elif (trans_angel > math.pi and trans_angel < (math.pi * 6) / 4):
            trans_angel -= math.pi
            tdx = -merge_xy * math.sin(trans_angel)
            tdy = math.sqrt(merge_xy ** 2 - tdx ** 2)
            return [tdx,tdy]
        elif (trans_angel > (math.pi * 6) / 4 and trans_angel < math.pi * 2):
            trans_angel = math.pi * 2 - trans_angel
            tdx = -merge_xy * math.sin(trans_angel)
            tdy = -math.sqrt(merge_xy ** 2 - tdx ** 2)
            return [tdx,tdy]

def sprite_pixel_collidet(sp1, sp2):
    # pixel collision detection
    rect1 = sp1.rect;       
    rect2 = sp2.rect;                            
    rect  = rect1.clip(rect2)
    x1 = rect.x-rect1.x
    y1 = rect.y-rect1.y
    x2 = rect.x-rect2.x
    y2 = rect.y-rect2.y
    # compare the 4th element of Color, if both of the iamges's 4th elements
    # are non_zero, then we can say that these images are pixel collisioned
    for r in xrange(0, rect.height):      
        for c in xrange(0, rect.width):
                if sp1.image.get_at((c+x1, r+y1))[3] & sp2.image.get_at((c+x2, r+y2))[3]:
                    return 1        
    return 0

def get_collidet_sg(sprite, group):
    # get the nearest sprite in a group from given sprite
    target_sprite = None

    nearest_dis = -1
    
    for s in group.sprites():
        if s.rect.colliderect(sprite.rect):     #colli with sprite
                if (sprite_pixel_collidet(s, sprite)):
                    dis = get_distance(sprite,s)
                    if (nearest_dis == -1 or nearest_dis > dis):
                            nearest_dis = dis
                            target_sprite = s

    return target_sprite

def get_collidet_sg_area(sprite, group, R):
    # given a Range R, return the sprites that
    # in the Range and the group at the same time
    target_sprites = []
    
    for s in group.sprites():
        dis = get_distance(s, sprite)
        if (dis < R):
            target_sprites.append(s)

    return target_sprites

def get_collidet_sg_pixel(sprite, group):
    target_sprites = []
    
    for s in group.sprites():
        if s.rect.colliderect(sprite.rect):
            if sprite_pixel_collidet(s, sprite):
                target_sprites.append(s)
                
    return target_sprites

def get_collidet_sg_laser(laser, group):
    target_sprites = []
    
    for s in group.sprites():
        if laser_in_rect(laser, s.rect):
            if sprite_pixel_collidet(s, laser):
                target_sprites.append(s)
                
    return target_sprites
    